/**
 * 
 */
package sg.edu.nus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 * @author Christian
 *
 */
public class RandomTestDataGenerator implements TestDataGenerator {

	// the number of roles to be generated
	private int roleCount = 0;
	// the number of users to be generated
	private int userCount = 0;
	// the total number of permissions
	private int permissionCount = 0;
	// the maximum number of roles a user can have
	private int maxRolesPerUser = 0;
	// the maximum number of permissions a role can have
	private int maxPermissionsPerRole = 0;
	private Map<Integer, Integer[]> originalRoles;
	private List<Integer[]> usersWithPermissions;
	
	
	
	public RandomTestDataGenerator(int roleCount, int userCount, int permissionCount,
			int maxRolesPerUser, int maxPermissionsPerRole) {
		super();
		this.roleCount = roleCount;
		this.userCount = userCount;
		this.permissionCount = permissionCount;
		this.maxRolesPerUser = maxRolesPerUser;
		this.maxPermissionsPerRole = maxPermissionsPerRole;
		
		System.out.println("TestDataGenerator: roleCount=" + roleCount + ", userCount=" + userCount + 
				", permissionCount=" + permissionCount + ", maxRolesPerUser=" + maxRolesPerUser +
				", maxPermissionsPerRole=" + maxPermissionsPerRole);
		
		// create roles
		originalRoles = createRolesWithPermissions();
		// create users
		usersWithPermissions = createUsersWithPermissions(originalRoles);
	}
	
	private Map<Integer, Integer[]> createRolesWithPermissions() {
		// create roles
		Map<Integer, Integer[]> res = new HashMap<Integer, Integer[]>();
		SortedSet<Integer> roleSet = null; 
		Integer[] role = null;
		int roleKey;
		while(true) {
			// check if enough roles created
			if(res.size() == roleCount) break;
			// create random number of permission for role, +1 because JAVA random is always < 1
			int nrt = (int)(Math.random() * (maxPermissionsPerRole + 1));
			// a role must have at least one permission
			nrt = Math.max(nrt, 1);
			// create new role
			roleSet = new TreeSet<Integer>();
			for(int i=0; i < nrt; i++) {
				// select random permission
				int permission = (int)(Math.random() * permissionCount);
				// add permission
				roleSet.add(permission);
			}
			// add role permissions
			role = new Integer[roleSet.size()];
			role = roleSet.toArray(role);
			roleKey = Arrays.hashCode(role);
			
			if(!res.containsKey(roleKey)) {
				res.put(roleKey, role);
			}
		}
		
		return res;
	}
	
	private List<Integer[]> createUsersWithPermissions(Map<Integer, Integer[]> rolesWithPermissions) {
		// create users
		List<Integer[]> res = new ArrayList<Integer[]>();
		SortedSet<Integer> userPermissionsSet = null;
		Integer[] userPermissions = null;
		List<Integer> roleKeyList = new ArrayList<Integer>(rolesWithPermissions.keySet());
		for(int user=0; user < userCount; user++) {
			// create roles list for user
			userPermissionsSet = new TreeSet<Integer>();
			// create random number of roles for user, +1 because JAVA random is always < 1
			int nrl = (int)(Math.random() * (maxRolesPerUser + 1));
			for(int i=0; i < nrl; i++) {
				// select random role
				int role = (int)(Math.random() * roleCount);
				// add permissions from roles
				userPermissionsSet.addAll(Arrays.asList(rolesWithPermissions.get(roleKeyList.get(role))));
				//System.out.println("user=" + user + ", role-id=" + role + ", role: " + rolesWithPermissions.get(role));
			}
			// add user permissions
			userPermissions = new Integer[userPermissionsSet.size()];
			userPermissions = userPermissionsSet.toArray(userPermissions);
			res.add(userPermissions);
			//System.out.println("user=" + user + ", permissions: " + userPermissions + "\n");
		}
		
		return res;
	}
	
	public Map<Integer, Integer[]> getOriginalRoles() {
		return originalRoles;
	}

	public List<Integer[]> getUsersWithPermissions() {
		return usersWithPermissions;
	}
	
	public void dumpData() {
		int roleId = 0;
		System.out.println("\nTest roles with permissions:");
		for(Integer[] rolePermissions : originalRoles.values()) {
			System.out.println("Role=" + roleId + ", permissions: " + Arrays.toString(rolePermissions));
			roleId++;
		}
		int user = 0;
		System.out.println("\nTest users with permissions:");
		for(Integer[] userPermissions : getUsersWithPermissions()) {
			System.out.println("User=" + user + ", permissions: " + Arrays.toString(userPermissions));
			user++;
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if(args.length < 5) {
			System.out.println("Usage: #roleCount #userCount #permissionCount #maxRolesPerUser #maxPermissionsPerRole");
			System.exit(1);
		}

		RandomTestDataGenerator gen = new RandomTestDataGenerator(Integer.parseInt(args[0]), Integer.parseInt(args[1]), Integer.parseInt(args[2]), 
				Integer.parseInt(args[3]), Integer.parseInt(args[4]));
		gen.dumpData();
	}

}
